//
//  FilamentDebugTool.cpp
//  LightARGame
//
//  Created by 8000 on 2020/8/20.
//  Copyright © 2020 tencent. All rights reserved.
//

#include <stdio.h>
#include "FilamentDebugTool.h"

#include <image/KtxUtility.h>
#include <filament/Renderer.h>
#include <filament/RenderableManager.h>
#include <filament/Scene.h>
#include <filament/IndirectLight.h>
#include <filament/RenderTarget.h>
#include <filament/View.h>
#include <filament/RenderableManager.h>
#include <filament/TransformManager.h>
#include <filameshio/MeshReader.h>
#include <filament/LightManager.h>
#include <filament/VertexBuffer.h>
#include <gltfio/ResourceLoader.h>
#include <gltfio/Animator.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/MaterialProvider.h>
#include <utils/EntityManager.h>
#include <filament/RenderableManager.h>
#include <filament/MaterialEnums.h>
//#include <filamat/MaterialBuilder.h>
#include <filament/Box.h>
using filament::math::quatf;
using filament::Texture;
using filament::MaterialInstance;
using filament::math::quatf;
using filament::IndirectLight;

using utils::Entity;
using utils::EntityManager;
using filament::LightManager;
using filament::VertexBuffer;
using filament::IndexBuffer;
using filament::VertexAttribute;
using filament::Material;
using filament::math::mat4;
using filament::math::mat4f;
using filament::math::float2;
using filament::math::float3;
using filament::math::float4;
using filamesh::MeshReader;
using filament::Box;
using filament::RenderableManager;
//filament::Material * FilamentDebugTool::genMat(filament::Engine *engine){
//	filamat::MaterialBuilder::init();
//	std::string customShader = R"(
//		void material(inout MaterialInputs material) {
//				prepareMaterial(material);
//				material.baseColor.rgba = materialParams.baseColor;
//		}
//	)";
//	filamat::MaterialBuilder builder = filamat::MaterialBuilder()
//				.name("debug_alix")
//				.require(filament::VertexAttribute::COLOR)
//				.shading(filament::Shading::UNLIT)
//				.culling(filament::backend::CullingMode::NONE)
//				.material(customShader.c_str())
//				.platform(filamat::MaterialBuilder::Platform::MOBILE)
//				.targetApi(filamat::targetApiFromBackend(engine->getBackend()));
//	builder.parameter(filamat::MaterialBuilder::UniformType::FLOAT4, "baseColor");
//	filamat::Package pkg = builder.build();
//	return filament::Material::Builder().package(pkg.getData(), pkg.getSize()).build(*engine);
//}

utils::Entity FilamentDebugTool::generateCubeEntity(float radius, float4 color, float3 position, filament::Engine *engine) {
	int vertexCount = 8;
	int indicesCount = 36;

	float3 *vertexs = new float3[vertexCount]{
			float3{-radius, radius,radius},
			float3{	radius, radius,radius},
			float3{	radius, -radius,radius},
			float3{-radius, -radius,radius},

			float3{-radius, radius, -radius},
			float3{	radius, radius, -radius},
			float3{	radius, -radius, -radius},
			float3{-radius, -radius, -radius},
	};

	uint16_t *indices = new uint16_t[indicesCount]{
			0, 3, 2, //前
			0, 2, 1,

			5, 6, 7, //后
			5, 7, 4,

			0, 4, 7, //左
			0, 7, 3,

			1, 2, 6, //右
			1, 6, 5,

			4, 0, 1, //顶
			4, 1, 5,

			6, 2, 3, //底
			6, 3, 7
	};

	filament::VertexBuffer *vb = VertexBuffer::Builder()
			.vertexCount(vertexCount)
			.bufferCount(1)
					//描述buffer内存布局,第二个参数是 buffer 索引
			.attribute(filament::VertexAttribute::POSITION,
								 0,
								 filament::VertexBuffer::AttributeType::FLOAT3,
								 0,//从缓冲区哪里开始 bufferOffset
								 sizeof(float3) //Stride 该属性数据段的间隔
			)
			.build(*engine);

	//给buffer设置数据
	vb->setBufferAt(*engine,
			//VertexBuffer::Builder() attribute 函数第二个参数设置的索引，表示给顶点的哪个属性设置数据
									0,
									filament::VertexBuffer::BufferDescriptor(vertexs,
																								 vertexCount * sizeof(float3), nullptr));

	filament::IndexBuffer *ib = filament::IndexBuffer::Builder().indexCount(indicesCount)
			.bufferType(IndexBuffer::IndexType::USHORT)
			.build(*engine);

	ib->setBuffer(*engine, IndexBuffer::BufferDescriptor(indices,
																											 indicesCount * sizeof(uint16_t), nullptr));
	Entity cubeEntity;
	cubeEntity = EntityManager::get().create();

	filament::Box aabb = filament::RenderableManager::computeAABB(vertexs, indices, vertexCount);

	filament::RenderableManager::Builder(1)
			.geometry(0, filament::RenderableManager::PrimitiveType::TRIANGLES, vb, ib,
								0,
								indicesCount)
			.boundingBox(aabb)
			.build(*engine, cubeEntity);

//	filament::Material *mat = genMat(engine);
//	filament::MaterialInstance *mat_instance = mat->createInstance();
//	mat_instance->setParameter("baseColor", filament::RgbaType::sRGB, color);
	 
//	filament::RenderableManager& rm = engine->getRenderableManager();
//	rm.setMaterialInstanceAt(rm.getInstance(cubeEntity), 0, mat_instance);
//
	filament::TransformManager& tcm = engine->getTransformManager();
	tcm.setTransform(tcm.getInstance(cubeEntity), mat4f::translation(position));
	
	return cubeEntity;
}

Entity FilamentDebugTool::generateLineEntity(std::vector<float3>points, float4 color, filament::Engine *engine){
	uint32_t vertexCount = (uint32_t)points.size();
	uint32_t indicesCount = (uint32_t)(2 * (points.size() - 1));
	float3 *vertexs = new float3[vertexCount];
	for (int i = 0; i < vertexCount; i++) {
		vertexs[i] = points[i];
	}
	uint16_t *indices = new uint16_t[indicesCount];
	int idx = 0;
	for (int i = 0; i < vertexCount; i++) {
		indices[idx++] = i;
		indices[idx++] = i + 1;
	}

	VertexBuffer *vb = VertexBuffer::Builder().vertexCount(vertexCount)
											 .bufferCount(1)
											 .attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3, 0, sizeof(vertexs[0]))
											 .build(*engine);

	vb->setBufferAt(*engine,
									 0,
									 VertexBuffer::BufferDescriptor(vertexs,
																									vertexCount * sizeof(vertexs[0]), nullptr));

	IndexBuffer *ib = IndexBuffer::Builder().indexCount(indicesCount)
										 .bufferType(IndexBuffer::IndexType::USHORT)
										 .build(*engine);

	ib->setBuffer(*engine, IndexBuffer::BufferDescriptor(indices, indicesCount * sizeof(uint16_t), nullptr));
	Entity entity = EntityManager::get().create();
	
	
	filament::Box aabb = filament::RenderableManager::computeAABB(vertexs, indices, vertexCount);
	filament::RenderableManager::Builder(1)
											 .geometry(0, filament::RenderableManager::PrimitiveType::LINES, vb, ib, 0, indicesCount)
											 .boundingBox(aabb)
											 .build(*engine, entity);

//	filament::Material *mat = genMat(engine);
//	filament::MaterialInstance *mat_instance = mat->createInstance();
//	mat_instance->setParameter("baseColor", filament::RgbaType::sRGB, color);
	 
//	filament::RenderableManager& rm = engine->getRenderableManager();
//	rm.setMaterialInstanceAt(rm.getInstance(entity), 0, mat_instance);
	 
	return entity;
}

Entity FilamentDebugTool::generateAlixEntity(filament::Engine *engine){
	int vertexCount = 8;
	float radius = 0.01;
	float len = 100;
	float3 *x_vertex_buffers = new float3[vertexCount]{
					float3{0, 0, radius},
					float3{0, radius, 0},
					float3{0, 0, -radius},
					float3{0, -radius, 0},
		
					float3{len, 0, radius},
					float3{len, radius, 0},
					float3{len, 0, -radius},
					float3{len, -radius, 0},
	};
	
	float3 *y_vertex_buffers = new float3[vertexCount]{
					float3{0, 0, radius},
					float3{radius, 0, 0},
					float3{0, 0, -radius},
					float3{-radius, 0, 0},
		
					float3{0, len, radius},
					float3{radius, len, 0},
					float3{0, len, -radius},
					float3{-radius, len, 0},
	};
	
	float3 *z_vertex_buffers = new float3[vertexCount]{
					float3{radius, 0, 0},
					float3{0, radius, 0},
					float3{-radius, 0, 0},
					float3{0, -radius, 0},
					float3{radius, 0, len},
					float3{0, radius, len},
					float3{-radius, 0, len},
					float3{0, -radius, len},
	};
	
	int indicesCount = 36;
	uint16_t *indices = new uint16_t[indicesCount]{
					0,3,2, //前
					0,2,1,

					5,6,7, //后
					5,7,4,

					0,4,7, //左
					0,7,3,

					1,2,6, //右
					1,6,5,

					4,0,1, //顶
					4,1,5,

					6,2,3, //底
					6,3,7
			};

	VertexBuffer *x_vb = VertexBuffer::Builder()
							.vertexCount(vertexCount)
							.bufferCount(1)
							.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3, 0, sizeof(float3))
							.build(*engine);

	x_vb->setBufferAt(*engine, 0, VertexBuffer::BufferDescriptor(x_vertex_buffers, vertexCount * sizeof(float3), nullptr));
	
	VertexBuffer *y_vb = VertexBuffer::Builder()
							.vertexCount(vertexCount)
							.bufferCount(1)
							.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3, 0, sizeof(float3))
							.build(*engine);

	y_vb->setBufferAt(*engine, 0, VertexBuffer::BufferDescriptor(y_vertex_buffers, vertexCount * sizeof(float3), nullptr));
	
	VertexBuffer *z_vb = VertexBuffer::Builder()
							.vertexCount(vertexCount)
							.bufferCount(1)
							.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3, 0, sizeof(float3))
							.build(*engine);

	z_vb->setBufferAt(*engine, 0, VertexBuffer::BufferDescriptor(z_vertex_buffers, vertexCount * sizeof(float3), nullptr));
	

	IndexBuffer *ib = IndexBuffer::Builder().indexCount(indicesCount)
										.bufferType(IndexBuffer::IndexType::USHORT)
										.build(*engine);

	ib->setBuffer(*engine, IndexBuffer::BufferDescriptor(indices, indicesCount * sizeof(uint16_t), nullptr));
	Entity alix_entity = EntityManager::get().create();
	
	filament::RenderableManager::Builder(3)
											.geometry(0, filament::RenderableManager::PrimitiveType::TRIANGLES, x_vb, ib,
																0,
																indicesCount)
											.geometry(1, filament::RenderableManager::PrimitiveType::TRIANGLES, y_vb, ib,
																0,
																indicesCount)
											.geometry(2, filament::RenderableManager::PrimitiveType::TRIANGLES, z_vb, ib,
																0,
																indicesCount)
											.receiveShadows(false)
											.culling(false)
											.castShadows(false)
											.build(*engine, alix_entity);
	
//	filament::Material *mat = FilamentDebugTool::genMat(engine);
//	filament::MaterialInstance *x_mat_instance = mat->createInstance();
//	x_mat_instance->setParameter("baseColor", filament::RgbaType::sRGB, float4{1,0,0,1});
//
//	filament::MaterialInstance *y_mat_instance = mat->createInstance();
//	y_mat_instance->setParameter("baseColor", filament::RgbaType::sRGB, float4{0,1,0,1});
//
//	filament::MaterialInstance *z_mat_instance = mat->createInstance();
//	z_mat_instance->setParameter("baseColor", filament::RgbaType::sRGB, float4{0,0,1,1});
	
//	filament::RenderableManager& rm = engine->getRenderableManager();
//	rm.setMaterialInstanceAt(rm.getInstance(alix_entity), 0, x_mat_instance);
//	rm.setMaterialInstanceAt(rm.getInstance(alix_entity), 1, y_mat_instance);
//	rm.setMaterialInstanceAt(rm.getInstance(alix_entity), 2, z_mat_instance);
	return alix_entity;
}

Entity FilamentDebugTool::generateWireFrameForEntity(Entity entity, filament::Engine *engine, bool transform_to_current){
	RenderableManager& rcm = engine->getRenderableManager();
	const Box& box = rcm.getAxisAlignedBoundingBox(rcm.getInstance(entity));
	
	filament::math::float3 max = box.getMax();
	filament::math::float3 min = box.getMin();
	filament::math::float3 half_extent = box.halfExtent;
		
	int indices_count = 24;
	int vertex_count = 8;
	//俯视XOZ平面，顺时针方向
	float3 *cube_vertexs = new float3[vertex_count]{
		float3{max.x - 2 * half_extent.x, max.y, max.z},
		float3{min.x, min.y + 2 * half_extent.y, min.z},
		float3{max.x, max.y, max.z - 2 * half_extent.z},
		max,
		
		float3{min.x, min.y, min.z + 2 * half_extent.z},
		min,
		float3{min.x + 2 * half_extent.x, min.y, min.z},
		float3{max.x, max.y - 2 * half_extent.y, max.z},
	};
	
	if (transform_to_current) {
		filament::TransformManager& tcm = engine->getTransformManager();
		mat4f t = tcm.getTransform(tcm.getInstance(entity));
		for (int i = 0; i < vertex_count; i++) {
			float3& p = cube_vertexs[i];
			p = (t * float4{p.x, p.y, p.z, 1}).xyz;
		}
	}
	
	uint16_t *line_indices = new uint16_t[indices_count]{
		0,1,
		1,2,
		2,3,
		3,0,
		4,5,
		5,6,
		6,7,
		7,4,
		0,4,
		1,5,
		2,6,
		3,7
	};
	
	filament::VertexBuffer *vb = VertexBuffer::Builder()
			.vertexCount(vertex_count)
			.bufferCount(1)
			.attribute(filament::VertexAttribute::POSITION,
								 0,
								 filament::VertexBuffer::AttributeType::FLOAT3,
								 0,
								 sizeof(float3)
			)
			.build(*engine);

	//给buffer设置数据
	vb->setBufferAt(*engine,
									0,
									filament::VertexBuffer::BufferDescriptor(cube_vertexs,
																								 vertex_count * sizeof(float3), nullptr));

	filament::IndexBuffer *ib = filament::IndexBuffer::Builder().indexCount(indices_count)
			.bufferType(IndexBuffer::IndexType::USHORT)
			.build(*engine);

	ib->setBuffer(*engine, IndexBuffer::BufferDescriptor(line_indices,
																											 indices_count * sizeof(uint16_t), nullptr));
	Entity cubeEntity;
	cubeEntity = EntityManager::get().create();
 
	filament::RenderableManager::Builder(1)
			.geometry(0, filament::RenderableManager::PrimitiveType::LINES, vb, ib,
								0,
								indices_count)
			.culling(false)
			.castShadows(false)
			.receiveShadows(false)
			.build(*engine, cubeEntity);

	return cubeEntity;
}

void FilamentDebugTool::updateTransformForEntity(utils::Entity entity, filament::Engine *engine, filament::math::mat4f transform){
  filament::TransformManager& tcm = engine->getTransformManager();
  tcm.setTransform(tcm.getInstance(entity), transform);
}
